class OlivePC extends SimplePC;

enum OliveInputType
{
	OIT_MoveCam,
	OIT_SpawnWall,
	OIT_SpawnLight,
	OIT_SpawnBomb,
	OIT_Max,
};

/** Holds the dimensions of the device's screen */
//var vector2D ViewportSize;

/** If TRUE, a new touch was detected (must be the only touch active) */
var bool bPendingTouch;

/** Holds the handle of the most recent touch */
var int PendingTouchHandle;

/** Holds the Actor that was selected */
var Actor SelectedActor;

/** Maximum distance an Actor can be to be picked */
var float PickDistance;

/** Maximum amount the mouse can move between touch and untouch to be considered a 'click' */
var float ClickTolerance;

var transient OlivePlayerInput OPI;

var transient TouchData BeginTouchData;

var transient bool bFollowPlayer;

var transient Vector2D LastTouchLocation;
var transient float LastTouchZ;

var transient Vector2D StartTouchLocation;
var transient bool bStartedTouchToCreateWall;
var transient bool bHasReachMinSlipDist;
var transient Vector2D MinSlipLoc2D;

//only exists one at one time ?
var transient OliveMagicWall MagicWall;

var transient OliveProj_TouchBomb   AttachedActor;

var transient OliveMagicLight MagicLight;

var OliveInputType CurrentOliveInputType;

var transient OliveInputType            CachedInputType;

var transient OliveActionData   Action_Saw;

//function SetupZones()
//{
//	super.SetupZones();
//	FreeLookZone.OnProcessInputDelegate = D_OnProcessInputDelegate;
//}

//function bool D_OnProcessInputDelegate(MobileInputZone Zone, float DeltaTime, int Handle, ETouchType EventType, Vector2D TouchLocation)
//{
//	switch(EventType)
//	{
//	case Touch_Began:
//		break;
//	case Touch_Moved:
//		break;
//	case Touch_Ended:
//		AttachedActor.bAttachedToMouse = false;
//		break;
//	case Touch_Cancelled:
//		break;

//	}
//	return true;
//}

function vector GetTracePosFrom2DVec(Vector2D TouchLocation)
{
	local Vector TouchOrigin, TouchDir;
	local Vector HitLocation, HitNormal;
	local Actor PickedActor;

	//Transform absolute screen coordinates to relative coordinates
	TouchLocation.X = TouchLocation.X / ViewportSize.X;
	TouchLocation.Y = TouchLocation.Y / ViewportSize.Y;
   
	//Transform to world coordinates to get pick ray
	LocalPlayer(Player).Deproject(TouchLocation, TouchOrigin, TouchDir);
  
	//Perform trace to find touched actor
	PickedActor = Trace(HitLocation, HitNormal, TouchOrigin + (TouchDir * PickDistance), TouchOrigin, true, , , TRACEFLAG_Bullet);
	if(PickedActor == none)
	{
		`log("touch actor is none");
		return TouchOrigin;
	}
	return HitLocation;
}

function ProcessSaw(Vector2D TouchLocation, ETouchType Type)
{
	local Vector2D TouchPointLocation;

	local Vector2D vectTmp;
	local float AngleRad;
	local float fTmp1, fTmp2;

	if (Action_Saw == none)
	{
		Action_Saw = new(self) class'OliveActionData';
	}
	//else
	//{
		switch( Type )
		{
		case Touch_Began:
		//case Touch_Stationary:  // Need this?
		case Touch_Moved:
			TouchPointLocation = vect2d(TouchLocation.X / ViewportSize.X, TouchLocation.Y / ViewportSize.Y);
			Action_Saw.TouchPointLastLast = Action_Saw.TouchPointLast;
			Action_Saw.TouchPointLast = Action_Saw.TouchPointCurrent;
			Action_Saw.TouchPointCurrent = TouchPointLocation;

			if (    Action_Saw.TouchPointLastLast == Action_Saw.TouchPointLast 
				|| Action_Saw.TouchPointLast == Action_Saw.TouchPointCurrent 
				|| Action_Saw.TouchPointCurrent == Action_Saw.TouchPointLastLast)
			{
				return;
			}

			vectTmp = Action_Saw.TouchPointLastLast - Action_Saw.TouchPointLast;
			fTmp1 = Atan2(vectTmp.Y, vectTmp.X);
			vectTmp = Action_Saw.TouchPointCurrent - Action_Saw.TouchPointLast;
			fTmp2 = Atan2(vectTmp.Y, vectTmp.X);
			AngleRad = Abs(FindDeltaAngle(fTmp1, fTmp2));

			if (AngleRad < Pi/2) //90 deg
			{
				//mark TouchPointLast as new "turn point"
				//if the distance between it and last turn point has reached the threshold, then it is a valid "saw input"
				if (Action_Saw.TurnPointLast != vect2d(0, 0))
				{
					vectTmp = Action_Saw.TurnPointLast - Action_Saw.TouchPointLast;
					if (Sqrt(Square(vectTmp.X) + Square(vectTmp.Y)) > Action_Saw.DistanceThreshold)
					{
						Action_Saw.bInput = true;
					}
				}
				Action_Saw.TurnPointLast = Action_Saw.TouchPointLast;
			}
			break;
		default:
			break;
		}

	//}
}

function PlayerTick(float DeltaTime)
{
	ActionTick(DeltaTime);
	super.PlayerTick(DeltaTime);
}

function ActionTick(float DeltaTime)
{
	if(CurrentOliveInputType == OIT_SpawnBomb )
	{
		Action_Saw.Tick(DeltaTime);
		if (Action_Saw.bSuccess)
		{
			ActionFinished(true);
		}
	}
}

function ActionFinished(bool bSuccess)
{
	Action_Saw.ResetActivity();
	//`log("Should Change the Mesh of the attached actor!!!!!!!!");
	AttachedActor.ChangeToBomb();
}
state PlayerWalking
{
	function PlayerMove( float DeltaTime )
	{
		local vector			X,Y/*,Z*/, NewAccel;
		local eDoubleClickDir	DoubleClickMove;
		local rotator			OldRotation;
		local bool				bSaveJump;

		if( Pawn == None )
		{
			GotoState('Dead');
		}
		else
		{
			//GetAxes(Pawn.Rotation,X,Y,Z);
			//Z = vect(0, 0, 1);
			X = vect(0.707, -0.707, 0);
			Y = vect(0.707, 0.707, 0);

			// Update acceleration.
			NewAccel = PlayerInput.aForward*X + PlayerInput.aStrafe*Y;
			NewAccel.Z	= 0;
			NewAccel = Pawn.AccelRate * Normal(NewAccel);

			if (IsLocalPlayerController())
			{
				AdjustPlayerWalkingMoveAccel(NewAccel);
			}

			DoubleClickMove = PlayerInput.CheckForDoubleClickMove( DeltaTime/WorldInfo.TimeDilation );

			// Update rotation.
			OldRotation = Rotation;
			UpdateRotation( DeltaTime );
			bDoubleJump = false;

			if( bPressedJump && Pawn.CannotJumpNow() )
			{
				bSaveJump = true;
				bPressedJump = false;
			}
			else
			{
				bSaveJump = false;
			}

			if( Role < ROLE_Authority ) // then save this move and replicate it
			{
				ReplicateMove(DeltaTime, NewAccel, DoubleClickMove, OldRotation - Rotation);
			}
			else
			{
				ProcessMove(DeltaTime, NewAccel, DoubleClickMove, OldRotation - Rotation);
			}
			bPressedJump = bSaveJump;
		}
	}
}

//change function name to fit its use better -cty
function SpawnTouchBomb()
{
	//local OliveCamera Camera;
	//local Vector          Loc;

	if(AttachedActor == none || !AttachedActor.bAttachedToMouse)
	{
		//Camera = OliveCamera(PlayerCamera);
		//Loc = Camera.Location;
		//Loc = Camera.ViewTarget.POV.Location;
		//Loc.Z -= 200;
		AttachedActor = Spawn( class'OliveProj_TouchBomb', Self);
		//AttachedActor.SetPhysics( PHYS_Falling );
		AttachedActor.bAttachedToMouse = true;
		CachedInputType = CurrentOliveInputType;
	}
}
event InitInputSystem()
{
	super.InitInputSystem();

	`log(GetFuncName()@self@PlayerInput);
	OPI = OlivePlayerInput(PlayerInput);
	SetGameInputType(OIT_MoveCam);

	LocalPlayer(Player).ViewportClient.GetViewportSize(ViewportSize);
}

function OnMoveCamTouch(int Handle, ETouchType Type, Vector2D TouchLocation, float DeviceTimestamp, int TouchpadIndex)
{
	local OliveCamera Cam;
	local Vector2D RelativeVect2D;
	local Vector RelativeVect;
	local float RelativeMoveFactor; //TODO put in default / global
	local float Move2D_X, Move2D_Y;

	Cam = OliveCamera(PlayerCamera);
	RelativeMoveFactor = 2;

	if(Type == Touch_Began)
	{
		//check if click has been handled by hud

		if(Cam.bUseAlternateWatchLocation)
		{
			Cam.bStartToResetCam = false;
			//Camera.RatioForResetCam = 0.f;
		}
		LastTouchLocation = TouchLocation;

		if (OliveHud(myHUD).ProcessHudClick(TouchLocation))
		{
			return;
		}
	}
	else if(Type == Touch_Moved)
	{
		if (!Cam.bUseAlternateWatchLocation)
		{
			Cam.bUseAlternateWatchLocation = true;
			Cam.WatchLocationAlternate = Cam.ViewTarget.POV.Location + Vector(Cam.ViewTarget.POV.Rotation) * Cam.FreeCamDistance;
		}

		//check if click has been handled by hud
		//if (OliveHud(myHUD).ProcessHudClick(TouchLocation))
		//{
		//	return;
		//}

		//`log("calc relative vector"@TouchLocation.X@TouchLocation.Y);
		RelativeVect2D = TouchLocation - LastTouchLocation;
		RelativeVect.X = RelativeVect2D.X;
		RelativeVect.Y = RelativeVect2D.Y;


		Move2D_X = RelativeVect.X * RelativeMoveFactor;
		Move2D_Y = RelativeVect.Y * RelativeMoveFactor;

		Cam.WatchLocationAlternate.X += - Move2D_X * Sqrt(2)/2 + Move2D_Y * Sqrt(2)/2;
		Cam.WatchLocationAlternate.Y += - Move2D_X * Sqrt(2)/2 - Move2D_Y * Sqrt(2)/2;
		LastTouchLocation = TouchLocation;
	}
}

function OnSpawnWallTouch(int Handle, ETouchType Type, Vector2D TouchLocation, float DeviceTimestamp, int TouchpadIndex)
{
	local Vector2D RelativeVect2D, SecondRelativeVect2D, FinalSlipVect2D, MinSlipVect2D;
	local Vector RelativeVect, SecondRelativeVect, FinalSlipVect, MinSlipVect, WallLoc, StartTouchLocation3D,WallVect;
	local Rotator WallRot;
	local float RelMoveDist, CosAngle, SecondRelMoveDist;
	local OliveCamera Cam;

	Cam = OliveCamera(PlayerCamera);

	if(Type == Touch_Began)
	{
		if (OliveHud(myHUD).ProcessHudClick(TouchLocation))
		{
			return;
		}
		if(Cam.bUseAlternateWatchLocation)
		{
			Cam.bStartToResetCam = false;
			//Camera.RatioForResetCam = 0.f;
		}
		else
		{
			//`log("[Touch Begin] Happens"@TouchLocation.X@TouchLocation.Y);
			bStartedTouchToCreateWall = true;
			StartTouchLocation = TouchLocation;
		}
	}
	else if(Type == Touch_Moved)
	{
		//if (OliveHud(myHUD).ProcessHudClick(TouchLocation))
		//{
		//	return;
		//}
		if(bStartedTouchToCreateWall)
		{
			RelativeVect2D = TouchLocation - StartTouchLocation;
			RelativeVect.X = RelativeVect2D.X;
			RelativeVect.Y = RelativeVect2D.Y;
			RelMoveDist = VSize2D(RelativeVect);
			//`log("[Touch_Moved] Slip Distance"@RelMoveDist@RelativeVect.X@RelativeVect.Y);
			if(!bHasReachMinSlipDist)
			{
				if(RelMoveDist > 6)
				{
					MinSlipLoc2D = TouchLocation;
					bHasReachMinSlipDist = true;
				}
			}
			else
			{
				SecondRelativeVect2D = TouchLocation - MinSlipLoc2D;
				SecondRelativeVect.X = SecondRelativeVect2D.X;
				SecondRelativeVect.Y = SecondRelativeVect2D.Y;

				SecondRelMoveDist = VSize2D(SecondRelativeVect);
				SecondRelativeVect = Normal(SecondRelativeVect);
				if(SecondRelMoveDist > 40)
				{
					MinSlipVect2D = MinSlipLoc2D - StartTouchLocation;
					MinSlipVect.X = MinSlipVect2D.X;
					MinSlipVect.Y = MinSlipVect2D.Y;

					MinSlipVect = Normal(MinSlipVect);
					CosAngle = MinSlipVect dot SecondRelativeVect;
					if(CosAngle <= 0.8f && CosAngle > -0.8f)
					{
						//`log("[Touch_Moved] cos angle <= 0.8 && > -0.8f, just return"@CosAngle);
						bStartedTouchToCreateWall = false;
						return ;
					}
				}

				if(SecondRelMoveDist > 100)
				{
					//Perform trace to find touched actor
					WallLoc = GetTracePosFrom2DVec(TouchLocation);
					StartTouchLocation3D = GetTracePosFrom2DVec(StartTouchLocation);

					//we cound spawn the wall
					bStartedTouchToCreateWall = false;
					FinalSlipVect2D = TouchLocation - StartTouchLocation;
					FinalSlipVect.X = FinalSlipVect2D.X;
					FinalSlipVect.Y = FinalSlipVect2D.Y;

					FinalSlipVect = Normal(FinalSlipVect);

					WallVect= Normal(WallLoc - StartTouchLocation3D);
					WallVect.Z = 0;
					WallRot = Rotator(WallVect);
					//`log("Spawn wall rot"@WallRot.Yaw@WallRot.Pitch@WallRot.Roll);
					if(MagicWall != none)
					{
						MagicWall.Destroy();
						MagicWall = none;
					}
					MagicWall = Spawn(class'OliveMagicWall', self, , StartTouchLocation3D, WallRot, ,true);
					MagicWall.LifeSpan = 5.f;
				}   
			}
		}
	}
	else if(Type == Touch_Ended)
	{
		bHasReachMinSlipDist = false;
		StartTouchLocation.X = 0.f; StartTouchLocation.Y = 0.f;
	}
}

function OnSpawnLightTouch(int Handle, ETouchType Type, Vector2D TouchLocation, float DeviceTimestamp, int TouchpadIndex)
{
	//local OliveCamera Cam;
	local vector2D NomarlizeTouchLoc, LightLoc2D;
	local vector LightLoc, TouchDir, StartLoc, EndLoc, HitNormal;
	local float DistOfTouchAndLight2D, ZScaleVal;
	local Actor HitActor;

	//Cam = OliveCamera(PlayerCamera);
	//Transform absolute screen coordinates to relative coordinates
	//NomarlizeTouchLoc = TouchLocation;
	NomarlizeTouchLoc.X = TouchLocation.X / ViewportSize.X;
	NomarlizeTouchLoc.Y = TouchLocation.Y / ViewportSize.Y;
   
	//Transform to world coordinates to get pick ray
	LocalPlayer(Player).Deproject(NomarlizeTouchLoc, StartLoc, TouchDir);
	HitActor = Trace(EndLoc, HitNormal, StartLoc + (TouchDir * PickDistance), StartLoc, true, , , TRACEFLAG_Bullet);
	if(HitActor == none)
	{
		`log("Hit Actor is none");
		return ;
	}
	ZScaleVal = 25.f / TouchDir.Z;
	TouchDir *= ZScaleVal;
	LightLoc = EndLoc + TouchDir;
	//LightLoc = GetTracePosFrom2DVec(TouchLocation);
	//LightLoc.Z -= 10.0f;

	if(Type == Touch_Began)
	{
		if (OliveHud(myHUD).ProcessHudClick(TouchLocation))
		{
			return;
		}
		//spawn light and attach to the touch pos
		if(MagicLight == none)
		{
			MagicLight = Spawn(class'OliveMagicLight', self, , LightLoc);
			MagicLight.AttachPC = self;
		}
		else
		{
			//detect whether there has light object near the touch location
			LightLoc2D = LocalPlayer(Player).Project(MagicLight.Location);
			LightLoc2D.X *= ViewportSize.X;
			LightLoc2D.Y *= ViewportSize.Y;
			DistOfTouchAndLight2D = Square(LightLoc2D.X - TouchLocation.X) + Square(LightLoc2D.Y - TouchLocation.Y);
			//`log("DistOfTouchAndLight2D ="@DistOfTouchAndLight2D);
			//can we use any calc way to git rid of this hard code val?
			if(DistOfTouchAndLight2D < 900)
			{
				MagicLight.AttachPC = self;
			}
		}
	}
	else if(Type == Touch_Moved)
	{
		if(MagicLight != none && MagicLight.AttachPC != none)
		{
			`log(GetFuncName()@LightLoc);
			MagicLight.SetLocation(LightLoc);
		}
	}
	else if(Type == Touch_Ended)
	{
		//drop the light at touch end pos
		if(MagicLight != none)
			MagicLight.AttachPC = none;
	}
}

function OnSpawnBombTouch(int Handle, ETouchType Type, Vector2D TouchLocation, float DeviceTimestamp, int TouchpadIndex)
{
	local Vector2D RelativeVect2D;
	local Vector RelativeVect, ActorNewLoc;
	local float RelativeMoveFactor; 
	local float Move2D_X, Move2D_Y;

	RelativeMoveFactor = 2;
	OliveHud(myHUD).ProcessHudClick(TouchLocation);

	switch(Type)
	{
	case Touch_Began:
		`log("[Touch Begin] Happens"@TouchLocation.X@TouchLocation.Y);
		LastTouchLocation = TouchLocation;
		ActorNewLoc = GetTracePosFrom2DVec(TouchLocation);
		ActorNewLoc.Z = Pawn.Location.Z;
		AttachedActor.SetLocation(ActorNewLoc);
		break;
	case Touch_Moved:
		if (OliveHud(myHUD).ProcessHudClick(TouchLocation))
		{
			return;
		}
		else
		{
			RelativeVect2D = TouchLocation - LastTouchLocation;
			RelativeVect.X = RelativeVect2D.X;
			RelativeVect.Y = RelativeVect2D.Y;
			Move2D_X = RelativeVect.X * RelativeMoveFactor;
			Move2D_Y = RelativeVect.Y * RelativeMoveFactor;
			ActorNewLoc.X = AttachedActor.Location.X +(  Move2D_X * Sqrt(2)/2 - Move2D_Y * Sqrt(2)/2 );
			ActorNewLoc.Y = AttachedActor.Location.Y +(  Move2D_X * Sqrt(2)/2 + Move2D_Y * Sqrt(2)/2 );
			ActorNewLoc.Z = AttachedActor.Location.Z;
			AttachedActor.SetLocation(ActorNewLoc);
			LastTouchLocation = TouchLocation;
		}
		ProcessSaw(TouchLocation, Type);
		break;
	case Touch_Cancelled:
	case Touch_Ended:
		AttachedActor.bAttachedToMouse = false;
		SetGameInputType(CachedInputType);
		break;
	default:
		break;
	}
}

function SetGameInputType(OliveInputType Type)
{
	CurrentOliveInputType = Type;
	if(OPI == none)
		OPI = OlivePlayerInput(PlayerInput);

	if(Type == OIT_MoveCam)
	{
		OPI.OnInputTouch = OnMoveCamTouch;
	}
	else if(Type == OIT_SpawnWall)
	{
		OPI.OnInputTouch = OnSpawnWallTouch;
	}
	else if(Type == OIT_SpawnLight)
	{
		OPI.OnInputTouch = OnSpawnLightTouch;
	}
	else if(Type == OIT_SpawnBomb)
	{
		OPI.OnInputTouch = OnSpawnBombTouch;
	}
}

function OnToggleSpawnWall( SeqAct_ToggleSpawnWall inAction )
{
	local OliveCamera OCam;
	OCam = OliveCamera(PlayerCamera);
	if( inAction.InputLinks[0].bHasImpulse )
	{
		OCam.YawAngle = -90.0f;
		OCam.PitchAngle = -90.0f;
		OCam.bUseAlternateWatchLocation = true;
		SetGameInputType(OIT_SpawnWall);
	}
	else
	if( inAction.InputLinks[1].bHasImpulse )
	{
		OCam.YawAngle = -45.0f;
		OCam.PitchAngle = -55.0f;
		OCam.bUseAlternateWatchLocation = false;
		SetGameInputType(OIT_MoveCam);
	}
}

function OnResetCamera( SeqAct_ResetCamera inAction )
{
	if( inAction.InputLinks[0].bHasImpulse )
	{
		OliveCamera(PlayerCamera).ResetCameraToCenter();
	}
}

simulated event PostBeginPlay()
{
	super.PostBeginPlay();
	Action_Saw = new(self) class'OliveActionData';
}

DefaultProperties
{
	CurrentOliveInputType=OIT_MoveCam
	PickDistance=10000.f
	//CameraClass=class'OliveCamera'
	InputClass=class'OlivePlayerInput'
	//CheatClass=class'OliveCheatManager'
}
